import { memo, useEffect, useRef, useState } from 'react'
import './index.scss'
import { ConfirmDialog, MyButton, MyDialog, MyInput, MySelect } from '../../components'
import http from '../../utils/http'
import api from '../../api'
import { SearchOutlined, PlayCircleOutlined, PauseCircleOutlined, PlusOutlined, SettingOutlined, MoreOutlined, MinusOutlined } from '@ant-design/icons';
import classNames from 'classnames'
import { formateTime } from '../../utils'
import { useAppDispatch, useAppSelector } from '../../utils/storeHook/storeHook'
import { addToNextPlay, setActMusicInfo, setPlayList } from '../../store/modules/musicStore'
import { Dropdown, Empty, message, Skeleton, Spin, Tooltip } from 'antd'

import { suggestPlaylists } from './suggestPlaylists'
import PlaylistInfo from './components/PlaylistInfo'

const musicToken = import.meta.env.VITE_YUN_MUSIC_TOKEN
const cookie = import.meta.env.VITE_YUN_MUSIC_COOKIE

interface musicT{
    id:string|number,
    name:string,
    artist:string,
    coverUrl?:string,
    time?:number|null,
    url?:string,
}

interface playListT{
    id:string|number,
    name:string,
    artist:string,
}

interface myPlaylistT{
    playlist_id:string,
    user_id:string,
    name:string,
    description:string,
    cover_url:string,
    is_public:string,
    created_at:string,
    updated_at:string,
    songs:string,
}

interface activePlaylistInfoT{
    id:string|number,
    name:string,
    descripe:string,
    coverUrl:string,
}


const MusicPage = memo(()=>{
    const [messageApi,contextHolder] = message.useMessage()

    const actMusicInfo = useAppSelector(state=>state.music.actMusicInfo)
    const dispatch = useAppDispatch()

    const [searchVal,setSearchVal] = useState<string>('')
    const [musicList,setMusicList] = useState<musicT[]>([])
    const [activeListId,setActiveList] = useState<string>('3778678')
    const [isPageLoading,setIsPageLoading] = useState<boolean>(false)
    const [isLoading,setIsLoading] = useState<boolean>(false)
    const [playListType,setPlayListType] = useState<string>('1') //1:推荐,2:我的
    const [myPlaylists,setMyPlaylists] = useState<myPlaylistT[]>([])
    const [addToPlayListDlgVis,setAddToPlayListDlgVis] = useState<boolean>(false)
    const [confirmDelVis,setConfirmDelVis] = useState<boolean>(false)
    const [createPlaylistDlgVis,setCreatePlaylistDlgVis] = useState<boolean>(false)
    const [isPlaylistInfoEditing,setIsPlaylistInfoEditing] = useState<boolean>(false)
    const [newPlaylistName,setNewPlaylistName] = useState<string>('')
    const [selectAddToPlaylistId,setSelectAddToPlaylistId] = useState<string>('')
    const [activePlaylistInfo,setActivePlaylistInfo] = useState<activePlaylistInfoT>({
        id:'3778678',
        descripe:'网易云音乐',
        name: '云音乐热歌榜',
        coverUrl: 'http://p1.music.126.net/0SUEG8yDACfx0Bw2MYFv4Q==/109951170048519512.jpg?param=40y40'
    })

    const addToPlaylistStateRef = useRef<{value:string,options:{value:string,label:string}[],songId:string|number}>({
        value:'',
        options:[],
        songId:''
    })
    const musicListRef = useRef<HTMLDivElement>(null)
    const requestStateRef = useRef<any>({
        pending: new Set(),
        completed: new Set(),
        timeouts: {},
    }) 

    // 懒加载观察者
    const observerRef = useRef<IntersectionObserver | null>(null)

    function changeVal(val:string){
        setSearchVal(val)
    }

    async function getMusicUrl(id:string|number){
        try{
            const detailRes = await http.post(api.music.getMusicDetail,{token:musicToken,id:id})
            const urlRes = await http.post(api.music.getMusicUrl,{token:musicToken,id:id,cookie:cookie})
            const musicInfo = {
                id:detailRes.data.songs[0].id,
                name:detailRes.data.songs[0].name,
                artist:detailRes.data.songs[0].ar.map((item:any)=>item.name).join('/'),
                coverUrl:detailRes.data.songs[0].al.picUrl,
                time:detailRes.data.songs[0].dt,
                url:urlRes.data.url
            }
            dispatch(setActMusicInfo(musicInfo))
            setIsLoading(false)
        }catch(error:any){
            messageApi.error(error.message)
            setIsLoading(false)
        }
    }

    function getMusicList(id:string) { 
        setIsPageLoading(true)
        if(musicList.length > 0) unobserveEle()
        http.post(api.music.getMusicList,{token:musicToken,id:id}).then(res=>{
            const resData = res.data
            const musicList = resData.playlist.map((item:any)=>{
                return {
                    ...item,
                    artist: item.user_name
                }
            })
            const item = suggestPlaylists.find(item=>item.id == id)
            if(item){
                setActivePlaylistInfo({
                    id,
                    name:item.name,
                    coverUrl: resData.avatar_url as string,
                    descripe:'网易云音乐'
                })
            }
            setMusicList(musicList)
            localStorage.setItem('actMusicListType',JSON.stringify(id))
            setActiveList(id)
        }).finally(()=>{
            setIsPageLoading(false)
        })
    }

    function getMyMusicList(item:myPlaylistT){
        const songsList = JSON.parse(item.songs) as musicT[]
        setActivePlaylistInfo({
            id:item.playlist_id,
            name:item.name,
            coverUrl:item.cover_url,
            descripe:item.description?item.description:'暂无描述',
        })
        if(songsList instanceof Array){
            setMusicList(songsList)
        }else{
            setMusicList([])
        }
        setActiveList(item.playlist_id)
    }

    function handlePlayListWithAll(id:string|number){
        setIsLoading(true)
        const clickIndex = musicList.findIndex((item:musicT)=>item.id === id)
        function startPlaySong(clickIndex:number){
            const updataArr = JSON.parse(JSON.stringify(musicList))
            dispatch(setPlayList(updataArr))
            getMusicUrl(updataArr[clickIndex].id)
        }
        if(clickIndex == -1){
            startPlaySong(0)
        }else{
            startPlaySong(clickIndex)
        }
    }

    async function getMusicDetail(id:number){
        const state = requestStateRef.current
        if(state.completed.has(id) || state.pending.has(id)){
            // 清理定时器
            if (state.timeouts[id]) {
                clearTimeout(state.timeouts[id]);
                delete state.timeouts[id];
            }
            return
        }
        state.pending.add(id)
        await http.post(api.music.getMusicDetail,{token:musicToken,id:id}).then((res)=>{
            if(res.code >= 400){
                messageApi.error(res.message)
                state.pending.delete(id)
                return
            }
            setMusicList(prev=>prev.map(item=>{
                if(item.id === id){
                    item['coverUrl'] = res.data.songs[0].al.picUrl || null
                    item['time'] = res.data.songs[0].dt
                }
                return item
            }))
            state.completed.add(id);
        }).catch(()=>{
            state.pending.delete(id)
        }).finally(()=>{
            // 清理定时器
            if (state.timeouts[id]) {
                clearTimeout(state.timeouts[id]);
                delete state.timeouts[id];
            }
        })
    }

    function searchMusic(){
        setIsPageLoading(true)
        if(musicList.length > 0) unobserveEle()
        http.post(api.music.searchMusic,{token:musicToken,keyword:searchVal,limit:30,page:1}).then(res=>{
            const resData = res.data
            const musicList = resData.songs.map((item:any)=>{
                const song = {
                    id:item.id,
                    name:item.name,
                    artist:item.artists.map((citem:any)=>{
                        return citem.name
                    }).join('/'),
                    user_id:item.artists.map((citem:any)=>{
                        return citem.id
                    }).join('/'),
                }
                return song
            })
            setMusicList(musicList)
            setActiveList('')
        }).finally(()=>{
            setIsPageLoading(false)
        })
    }

    function unobserveEle(){
        musicList.forEach(item=>{
            requestStateRef.current.completed.delete(item.id)
            requestStateRef.current.pending.delete(item.id)
            const ele = document.querySelector(`#musicItem_${item.id}`) as HTMLElement
            if(!ele || !observerRef.current) return
            observerRef.current.unobserve(ele)
        })
    }

    function playNext(e: React.MouseEvent<HTMLDivElement, MouseEvent>,item:playListT){
        e.stopPropagation()
        dispatch(addToNextPlay(item))
    }

    async function showAddPlaylistDlg(e:any,id:string|number){
        e.domEvent.stopPropagation()
        setAddToPlayListDlgVis(true)
        addToPlaylistStateRef.current.songId = id
    }

    function handleCloseAddToPlayListDlg(){
        setAddToPlayListDlgVis(false)
        setSelectAddToPlaylistId('')
    }

    function showConfirmRemoveDlg(e:any,id:string|number){
        e.domEvent.stopPropagation()
        setConfirmDelVis(true)
        addToPlaylistStateRef.current.songId = id
    }

    function getPlayLists(){
        http.post(api.music.getPlayLists,{}).then(res=>{
            const resData = res.data as myPlaylistT[]
            setMyPlaylists(resData)
            addToPlaylistStateRef.current.options = resData.map(item=>{
                return {
                    value:item.playlist_id,
                    label:item.name,
                }
            })
        })
    }

    function handleCreateMyPlaylist(){
        const name = newPlaylistName
        if(!name) return messageApi.error('请输入歌单名称')
        http.post(api.music.addPlayList,{name}).then(()=>{
            setCreatePlaylistDlgVis(false)
            getPlayLists()
            messageApi.success('创建成功')
            setNewPlaylistName('')
        }).catch(err=>{
            messageApi.error(err.message)
        })
    }

    async function addSongToMyPlaylist(){
        if(!selectAddToPlaylistId) return messageApi.error('请选择歌单')
        const music = musicList.find(item=>item.id === addToPlaylistStateRef.current.songId)
        const playlist_id = selectAddToPlaylistId
        const thePlaylist = myPlaylists.find(item=>item.playlist_id === playlist_id)
        if(!thePlaylist) return messageApi.error('未知错误')
        if(music){
            const curSongs = (JSON.parse(thePlaylist.songs) as musicT[]) || []
            const isExist = curSongs.findIndex(item=>item.id == music.id)
            if(isExist != -1){
                return messageApi.error('歌曲已存在该歌单')
            }
            try{
                const detailRes = await http.post(api.music.getMusicDetail,{token:musicToken,id:music.id})
                const urlRes = await http.post(api.music.getMusicUrl,{token:musicToken,id:music.id,cookie:cookie})
                curSongs.unshift({
                    id:detailRes.data.songs[0].id,
                    name:detailRes.data.songs[0].name,
                    artist:detailRes.data.songs[0].ar.map((item:any)=>item.name).join('/'),
                    coverUrl:detailRes.data.songs[0].al.picUrl,
                    time:detailRes.data.songs[0].dt,
                    url:urlRes.data.url
                })
                const songs = JSON.stringify(curSongs)
                
                await http.post(api.music.editPlayList,{playlist_id:playlist_id,songs:songs})
                
                setSelectAddToPlaylistId('')
                setAddToPlayListDlgVis(false)
                getPlayLists()
                messageApi.success('添加成功')
            }catch(err){
                console.log(err);
                messageApi.error('发生错误')
            }
        }
    }

    async function removeSongFromMyPlaylist(){
        const music_id = addToPlaylistStateRef.current.songId
        const music = musicList.find(item=>item.id === music_id)
        const playlist_id = activeListId
        const thePlaylist = myPlaylists.find(item=>item.playlist_id === playlist_id)

        if(!thePlaylist) return messageApi.error('未知错误')

        if(music){
            const curSongs = (JSON.parse(thePlaylist.songs) as musicT[]) || []
            try{
                const songs = JSON.stringify(curSongs.filter(item=>item.id != music.id))

                await http.post(api.music.editPlayList,{playlist_id:playlist_id,songs:songs})

                getPlayLists()
                messageApi.success('成功移除该歌曲')
            }catch(err){
                console.log(err);
                messageApi.error('发生错误')
            }
        }
    }

    function handleShowEditPlaylistDlg(e: React.MouseEvent<HTMLDivElement, MouseEvent>){ 
        e.stopPropagation()
        setIsPlaylistInfoEditing(true)
    }

    function handleEditPlaylistInfo(name:string,descripe:string,cover_url?:string){
        if(!name){
            messageApi.error('请输入歌单名称')
            return
        }
        const submitForm = {
            cover_url,
            name,
            descripe,
            playlist_id:activePlaylistInfo.id
        }

        console.log(submitForm);
        
        http.post(api.music.editPlayList,submitForm).then(()=>{
            setActivePlaylistInfo({
                name:name?name:activePlaylistInfo.name,
                descripe:descripe?descripe:activePlaylistInfo.descripe,
                coverUrl:cover_url?cover_url:activePlaylistInfo.coverUrl,
                id:activePlaylistInfo.id
            })
            getPlayLists()
            setIsPlaylistInfoEditing(false)
            messageApi.success('修改成功')
        }).catch(err=>{
            messageApi.error(err.message)
            setIsPlaylistInfoEditing(false)
        })
    }

    useEffect(()=>{
        musicList.forEach(item=>{
            const ele = document.querySelector(`#musicItem_${item.id}`) as HTMLElement
            if(!observerRef.current || !ele) return
            observerRef.current.observe(ele)
        })
    },[musicList])

    useEffect(()=>{ 
        setIsPlaylistInfoEditing(false)
    },[activeListId])

    useEffect(()=>{
        observerRef.current = new IntersectionObserver((entries) => { 
            entries.forEach((entry) => { 
                if (entry.isIntersecting) { 
                    const id = entry.target.id.split('_')[1]
                    if(requestStateRef.current.timeouts[id]) return
                    requestStateRef.current.timeouts[id] = setTimeout(() => {
                        getMusicDetail(+id)
                        requestStateRef.current.timeouts[id] = null
                    }, 100);
                }
            });
        }, { threshold: 0.5 });
        const localMusicListType = JSON.parse(localStorage.getItem('actMusicListType') || '3778678')
        getMusicList(localMusicListType)
        
        getPlayLists()
        return ()=>{ 
            if(observerRef.current){
                observerRef.current.disconnect()
            }
        }
    },[])
    return (
        <>
            {contextHolder}
            <div className="music_box">
                <div className="music_header">
                    <div className="music_header_logo">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="24" height="24" filter="none">
                                <g>
                                <rect width="48" height="48" fill="currentColor" fill-opacity="0.01" stroke="none"/><path d="M30 34.5C30 32.567 31.567 31 33.5 31H41V34.4C41 36.3882 39.3882 38 37.4 38H33.5C31.567 38 30 36.433 30 34.5Z" fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"/><path d="M6 38.5C6 36.567 7.567 35 9.5 35H16V38.4C16 40.3882 14.3882 42 12.4 42H9.5C7.567 42 6 40.433 6 38.5Z" fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"/><path d="M16 18.044V18.044L41 12.125" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M16 38V10L41 4V33.6924" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
                                </g>
                            </svg>
                        <span>音乐系统</span>
                    </div>
                    <div className="music_header_search_container">
                        <MyInput value={searchVal} onChange={changeVal} height='40' placeholder='搜索歌曲、歌手' onEnter={searchMusic}></MyInput>
                        <i><SearchOutlined /></i>
                    </div>
                </div>
                <div className='music_main'>
                    <div className='music_main_left'>
                        <div className='music_main_left_header'>
                            <div 
                                className={classNames("music_main_left_header_action",{'active':playListType==='1'})}
                                onClick={()=>setPlayListType('1')}
                            >
                                排行榜
                            </div>
                            <div 
                                className={classNames("music_main_left_header_action",{'active':playListType==='2'})}
                                onClick={()=>setPlayListType('2')}
                            >
                                我的
                                <Tooltip title="新增歌单">
                                    <span 
                                        className='music_create_myPlaylist_btn'
                                        onClick={()=>setCreatePlaylistDlgVis(true)}
                                    ><PlusOutlined/></span>
                                </Tooltip>
                            </div>
                        </div>
                        <div className='music_main_left_playlists'>
                            {playListType=='1'?
                            <>{suggestPlaylists.length>0&&suggestPlaylists.map(item=>
                                <div 
                                    className={classNames('music_main_left_playlists_item', {'active':activeListId==item.id})} 
                                    key={item.id}
                                    onClick={()=>getMusicList(item.id)}
                                >
                                    <div className='music_playlists_item_cover'>
                                        <img src={item.coverUrl} loading='lazy' alt=""/>
                                    </div>
                                    <Tooltip title={item.name} placement='bottom'>
                                        <div className='music_playlists_item_name'>
                                            {item.name}
                                        </div>
                                    </Tooltip>
                                </div>
                            )}
                            </>
                            :<>{myPlaylists.length>0&&myPlaylists.map(item =>
                                <div
                                    className={classNames('music_main_left_playlists_item', {'active':activeListId==item.playlist_id})} 
                                    onClick={()=>getMyMusicList(item)}
                                >
                                    <div className='music_playlists_item_cover'>
                                        {item.cover_url?<img src={item.cover_url} loading='lazy' alt=""/>:
                                        <div className="music_cover">
                                        {item.cover_url?<img src={item.cover_url} alt="" />:
                                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="1em" height="1em" filter="none">
                                                
                                                <g>
                                                <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z" fill="rgba(16,16,16,1)"></path>
                                                </g>
                                            </svg>}
                                        </div>}
                                    </div>
                                    <Tooltip title={item.name} placement='bottom'>
                                        <div className='music_playlists_item_name'>
                                            {item.name}
                                        </div>
                                    </Tooltip>
                                    <div className="music_playlists_item_setting" onClick={handleShowEditPlaylistDlg}>
                                        <SettingOutlined  />
                                    </div>
                                </div>
                            )}
                            </>
                            }
                        </div>
                    </div>
                    {isPageLoading?<Skeleton style={{'flex': '1'}} />:
                    <div className="music_list_section">
                        <Spin 
                            size="large" 
                            tip="加载至播放列表..." 
                            spinning={isLoading}
                            fullscreen
                        />
                        <div className="music_list" ref={musicListRef}>
                            <PlaylistInfo 
                                onPlayListWithAll={handlePlayListWithAll}
                                activePlaylistInfo={activePlaylistInfo}
                                isEdit={isPlaylistInfoEditing}
                                onClose={()=>setIsPlaylistInfoEditing(false)}
                                onEdit={handleEditPlaylistInfo}
                            />
                            {musicList.length>0?musicList.map(item=>
                            <div 
                                className={classNames("music_item",{'playing':item.id===actMusicInfo?.id}) }
                                key={item.id}
                                id={'musicItem_'+item.id}
                                onClick={()=>handlePlayListWithAll(item.id)}
                            >
                                <div className="music_cover">
                                    {item.coverUrl?<img src={item.coverUrl} alt="" />:
                                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" filter="none">
                                        
                                        <g>
                                        <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z" fill="rgba(16,16,16,1)"></path>
                                        </g>
                                    </svg>}
                                    <div className='music_cover_playState'>{item.id===actMusicInfo?.id?<PauseCircleOutlined />:<PlayCircleOutlined />}</div>
                                </div>
                                <div className="music_info">
                                    <div className="music_title">{item.name}</div>
                                    <div className="music_artist">{item.artist}</div>
                                </div>
                                <div className="music_duration">
                                    {item.time?formateTime(+item.time):''}
                                </div>
                                <div className="music_actions">
                                    <Tooltip title='添加到下一首'>
                                            <div className="music_action_btn" onClick={(e)=>playNext(e,item)}>
                                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="1em" height="1em" filter="none">
                                                <g>
                                                <path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z" fill="currentColor"></path>
                                                </g>
                                            </svg>
                                        </div>
                                    </Tooltip>
                                    <Dropdown 
                                        menu={{
                                            items: [
                                                {
                                                    key: '1',
                                                    label: '添加到我的歌单',
                                                    onClick:(e:any)=>showAddPlaylistDlg(e,item.id),
                                                    icon: <PlusOutlined />
                                                },
                                                {
                                                    key: '2',
                                                    label: '移出歌单',
                                                    onClick:(e:any)=>showConfirmRemoveDlg(e,item.id),
                                                    icon: <MinusOutlined />
                                                }
                                            ]
                                        }}
                                        trigger={['click']}
                                        arrow={{ pointAtCenter: true }}
                                        destroyOnHidden={true}
                                    >
                                        <div className="music_action_btn" onClick={(e:any)=>e.stopPropagation()}>
                                            <MoreOutlined />
                                        </div>
                                    </Dropdown>
                                </div>
                            </div>):
                            <Empty />}
                        </div>
                    </div>}
                </div>
            </div>
            <MyDialog title='添加到我的歌单' visible={addToPlayListDlgVis} onClose={handleCloseAddToPlayListDlg}>
                <div className='music_addToPlayList'>
                    <div className='music_addToPlayList_selector'>
                        <div className='music_addToPlayList_selector_input'>
                            <MySelect 
                                value={selectAddToPlaylistId}
                                options={addToPlaylistStateRef.current.options}
                                onChange={(value:any)=>setSelectAddToPlaylistId(value)}
                                placeholder='选择歌单'
                            ></MySelect>
                        </div>
                    </div>
                    <div className='music_addToPlayList_footer'>
                        <MyButton type='guest' onClick={handleCloseAddToPlayListDlg}>取消</MyButton>
                        <MyButton type='primary' onClick={addSongToMyPlaylist}>确认</MyButton>
                    </div>
                </div>
            </MyDialog>
            <MyDialog title='创建新歌单' visible={createPlaylistDlgVis} onClose={()=>setCreatePlaylistDlgVis(false)}>
                <div className='music_addToPlayList'>
                    <div className='music_addToPlayList_selector'>
                        <div className='music_addToPlayList_selector_input'>
                            <MyInput 
                                value={newPlaylistName} 
                                placeholder='输入歌单名称'
                                onChange={(value:any)=>setNewPlaylistName(value)}
                            />
                        </div>
                    </div>
                    <div className='music_addToPlayList_footer'>
                        <MyButton type='guest' onClick={() => setCreatePlaylistDlgVis(false)}>取消</MyButton>
                        <MyButton type='primary' onClick={handleCreateMyPlaylist}>确认</MyButton>
                    </div>
                </div>
            </MyDialog>
            <ConfirmDialog visible={confirmDelVis} onClose={()=>setConfirmDelVis(false)} onConfirm={removeSongFromMyPlaylist} />
        </>
    )
})

export default MusicPage